/**
 * `locate(index)` is a function that locate the source code position by index.
 * position is a object that contains line, column.
 * @param {string} source - source code
 * @returns {(index: number) => Position} - locate function
 */
export function createLocator(source) {
  const lines = source.split("\n");

  // monotonically increasing
  const line_offset_map = [0]; // 1-based
  for (let i = 0; i < lines.length; i++) {
    // here `1` is for line break `\n`
    line_offset_map[i + 1] = line_offset_map[i] + lines[i].length + 1;
  }

  /**
   * locate the source code position by index
   * @param {number} index - index of source code
   * @returns {Position} - position
   */
  return function locate(index) {
    if (index < 0 || index >= source.length) {
      throw new Error("index out of range");
    }

    let line = 0; // 0-based
    for (let i = 1; i < line_offset_map.length; i++) {
      if (line_offset_map[i] > index) {
        line = i - 1;
        break;
      }
    }

    return new Position(line, index - line_offset_map[line]);
    // {
    //   line,
    //   column: index - line_offset_map[line],
    // };
  };
}

class Position {
  /**
   * @param {number} line - line number 0-based
   * @param {number} column - column number 0-based
   */
  constructor(line, column) {
    this.line = line;
    this.column = column;
  }
}
